#ifndef __LEARN_OPENGL_WINDEVICE_H_INCLUDE__
#define __LEARN_OPENGL_WINDEVICE_H_INCLUDE__

#include "LDXConfig.h"
#include "IEventReceiver.h"
#include "IReferenceCounted.h"
//#include "CVideoDriver.h"
//#include "CSceneManager.h"
#include "mathtools\mathtools.h"
#include "SColorf.h"
#include "CTimer.h"
#include "IRenderDriver.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <mutex>

namespace ldx
{
	namespace scene
	{
		class CSceneManager;
	}

	namespace device
	{
		class CCursorControl;

		struct SmallClockTick
		{
			double TimeUntilNow = 0.0;
			const double DeltaTime = 0.01;
			double Accumulator = 0.0;
			double CurrentTime = 0;
			float RealDeltaTIme = 0.01f;
		};

		struct SWinParams
		{
			bool Resizable = false;
			int WinWidth = 720;
			int WinHeight = 480;
			int Bits = 32;
			bool Fullscreen = false;
			bool DepthBuffer = true;
			bool StencilBuffer = true;
			std::string Caption = "LearnDX";
			HWND WindowId;
			IEventReceiver* EventReceiver;
		};

		struct SMouseMultiClicks
		{
			SMouseMultiClicks()
				: DoubleClickTime(500), CountSuccessiveClicks(0), LastClickTime(0), LastMouseInputEvent(EMIE_COUNT)
			{}

			unsigned int DoubleClickTime;
			unsigned int CountSuccessiveClicks;
			unsigned int LastClickTime;
			mt::position2di LastClick;
			EMOUSE_INPUT_EVENT LastMouseInputEvent;
		};

		class WinDevice :public IReferenceCounted
		{
		public:

			//! static function:used to get the single device,threadsafe.
			static WinDevice* getWinDeviceInstance();

			//static function:runtime error msg.
			static	void onError(int errorCode, const char* msg);

			//! Construtor.
			WinDevice(const SWinParams& params);
			
			//! Constructor.
			WinDevice(std::string caption = "LearnDX", int width = 720, int height = 480,
				bool resizeable = false, bool fullscreen = false,bool depthbuffer= true,bool stencil=true,IEventReceiver* EventReceiver = 0);

			//! Destructor.
			~WinDevice();

			//! Run time clock and handle messages.
			bool run();

			//! Begin this drawing turn,used to clear the color buffer and other help buffer.
			void beginScene(render::E_CLEAR_FLAG flag,render::SColorf& color,float depth = 1.0f,uint stencil =0);
			
			//! End this drawing turn.
			void endScene();

			//! Can't be capyed.
			WinDevice(WinDevice& other) = delete;

			//! Can't be capyed.
			WinDevice& operator=(WinDevice& other) = delete;

			//! Get the render driver.
			render::IRenderDriver* getRenderDriver();

			//! Get the scene manager.
			scene::CSceneManager* getSceneManager();

			//! Get the main timer.
			CTimer* getTimer();

			CCursorControl* getCursorControl();
		
			//! Get the size of screen.
			mt::vector2dui getScreenSize();

			//! Get the size of the target render face size.
			mt::vector2di getCurrentRenderTargetSize();

			//! Is the screen fulled?
			bool isFullscreen();

			//! Is this window active,maybe can yield time.
			bool isWindowActive();

			//! Is this window minimized,maybe can yield time.
			bool isWindowMinimized();

			//! Give the cpu time up.
			void yield();

			//! Get the delta time of this turn.(scaled)
			float getDeltaTime();

			//! Get the delta time of this turn.(system)
			float getRealDeltaTime();

			//! Get current time.
			float getCurrentTime();

			//! Set window caption
			void setWindowCaption(std::string caption);

			//! send the event to the right receiver
			bool postEventFromUser(const SEvent& event);

			//! Compares to the last call of this function to return double and triple clicks.
			//! \return Returns only 1,2 or 3. A 4th click will start with 1 again.
			virtual uint checkSuccessiveClicks(int mouseX, int mouseY, EMOUSE_INPUT_EVENT inputEvent)
			{
				const int MAX_MOUSEMOVE = 3;

				uint clickTime = getTimer()->getTimeCycles();

				if ((clickTime - MouseMultiClicks.LastClickTime) < MouseMultiClicks.DoubleClickTime
					&& mt::abs_(MouseMultiClicks.LastClick.X - mouseX) <= MAX_MOUSEMOVE
					&& mt::abs_(MouseMultiClicks.LastClick.Y - mouseY) <= MAX_MOUSEMOVE
					&& MouseMultiClicks.CountSuccessiveClicks < 3
					&& MouseMultiClicks.LastMouseInputEvent == inputEvent
					)
				{
					++MouseMultiClicks.CountSuccessiveClicks;
				}
				else
				{
					MouseMultiClicks.CountSuccessiveClicks = 1;
				}

				MouseMultiClicks.LastMouseInputEvent = inputEvent;
				MouseMultiClicks.LastClickTime = clickTime;
				MouseMultiClicks.LastClick.X = mouseX;
				MouseMultiClicks.LastClick.Y = mouseY;

				return MouseMultiClicks.CountSuccessiveClicks;
			}

			//! Notifies the device, that it has been resized
			void OnResized();

			//! switchs to fullscreen
			bool switchToFullScreen(bool reset = false);

		private:
			//! Init window.
			bool initWindow();

			//! create window.
			bool createWindow();

			//private functions
			bool createRenderDriver();

			bool createGUIAndScene();

			void closeWindow();

			void update(float deltaTime);


			//! Process system events
			void handleSystemMessages();

			void getWindowsVersion(std::string& version);

			void resizeIfNecessary();

			mt::vector2di RenderTargetSize;
			// private params
			SWinParams WinParams;
			/*GLFWwindow* GFWindow;*/
			CCursorControl* CursorControl;

			render::IRenderDriver* RenderDriver;
			scene::CSceneManager* SceneManager;

			CTimer* Timer;
			SmallClockTick ClockTick;
			IEventReceiver* UserReceiver;
			static WinDevice* StaticWinDevice;
		
			//mutex
			static std::mutex Mut;

			HWND HWnd;
			bool Close;
			bool ChangedToFullScreen;
			bool Resized;
			bool ExternalWindow;
			CCursorControl* Win32CursorControl;
			DEVMODE DesktopMode;
			SMouseMultiClicks MouseMultiClicks;
		};


	}//end namespace device
}//end namespace ldx
#endif//! 